﻿using System;

namespace ASM6502
{
    public enum OpCode
    {
        ___ = 0xff,
        NOP,//空操作
        BRK = 0x00,//终止程序

        //传送
        TAX,//传送 累加器A->X寄存器
        TAY,//传送 累加器A->Y寄存器
        TXA,//传送 累加器A<-Y寄存器
        TYA,//传送 累加器A<-Y寄存器

        //清除与设置标志
        CLC,//进位
        CLD,//十进制
        CLI,//中断禁止
        CLV,//溢出
        SEC,
        SED,
        SEI,

        //算术操作
        ADC,    //进位加法
        SBC,    //借位减法

        //存取值指令
        LDA,
        LDX,
        LDY,
        STA,
        STY,
        STX,
        //STZ指令 6502 手册有，模拟器没有，好像可以被替代

        ora,
        asl,
        php,
        bpl,
        jsr,
        and,
        bit,
        rol,
        plp,
        bmi,
        rti,
        eor,
        lsr,
        pha,
        jmp,
        bvc,
        rts,
        ror,
        pla,
        bvs,
        dey,
        bcc,
        txs,
        bcs,
        tsx,
        cpy,
        cmp,
        dec,
        iny,
        dex,
        bne,
        cpx,
        inc,
        inx,
        beq,
    }
    public enum AddressMode
    {
        Absolute = 1,    // 1
        AbsoluteX,       // 2
        AbsoluteY,       // 3
        Accumulator,     // 4
        Immediate,       // 5
        Implied,         // 6
        IndexedIndirect, // 7
        Indirect,        // 8
        IndirectIndexed, // 9
        Relative,        // 10
        ZeroPage,        // 11
        ZeroPageX,       // 12
        ZeroPageY        // 13
    };


    public class Instruction
    {
        public override string ToString()
        {
            var info = code.ToString() + "  " + addressMode.ToString();
            if (Size > 1)
            {
                info += " [";
                if (Size > 1)
                    info += data.ToString("X02");
                if (Size > 2)
                    info += "," + data2.ToString("X02");
                info += "]";

                if (Size == 2)
                    info += ((sbyte)data).ToString() + "," + data;
                else
                    info += ((short)(ushort)(data | (uint)data2 << 8)).ToString() + "," + ((ushort)(data | (uint)data2 << 8)).ToString();
            }
            return info;
        }
        private Instruction()
        {

        }
        public OpCode code
        {
            get;
            private set;
        }
        public AddressMode addressMode
        {
            get;
            private set;
        }
        public byte data;
        public byte data2;
        public int Size
        {
            get;
            private set;
        }
        public int Cycles
        {
            get;
            private set;
        }
        public int PageCycles
        {
            get;
            private set;
        }
        public static Instruction CreateFromByte(byte b)
        {
            Instruction i = new Instruction();
            i.code = _instructions[b];
            i.addressMode = (AddressMode)_addressModes[b];
            i.Size = _instructionSizes[b];
            i.Cycles = _instructionCycles[b];
            i.PageCycles = _instructionPageCycles[b];
            return i;
        }
        public static Instruction CreateFromOpCode(OpCode code, AddressMode mode)
        {
            for (var b = 0; b < 256; b++)
            {
                if (_instructions[b] == code && _addressModes[b] == (int)mode)
                {
                    Instruction i = new Instruction();
                    i.code = code;
                    i.addressMode = mode;
                    i.Size = _instructionSizes[b];
                    i.Cycles = _instructionCycles[b];
                    i.PageCycles = _instructionPageCycles[b];
                    return i;
                }
            }
            throw new Exception("error addressmode and opcode pairs");
        }

        public static readonly int[] _instructionSizes = {
            1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
            3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
            1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
            1, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
            2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
            2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
        };
        public static readonly int[] _addressModes = {
            6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2,
            1, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2,
            6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 1, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2,
            6, 7, 6, 7, 11, 11, 11, 11, 6, 5, 4, 5, 8, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2,
            5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 13, 13, 6, 3, 6, 3, 2, 2, 3, 3,
            5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 13, 13, 6, 3, 6, 3, 2, 2, 3, 3,
            5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2,
            5, 7, 5, 7, 11, 11, 11, 11, 6, 5, 6, 5, 1, 1, 1, 1,
            10, 9, 6, 9, 12, 12, 12, 12, 6, 3, 6, 3, 2, 2, 2, 2,
        };
        public static readonly int[] _instructionCycles = {
            7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6,
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6,
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6,
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6,
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
            2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5,
            2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4,
            2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4,
            2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
            2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6,
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7,
        };
        public static readonly int[] _instructionPageCycles = {
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0,
        };

        public static readonly OpCode[] _instructions = new OpCode[256] {
            //  0           1           2           3           4           5           6    7    8    9    A    B    C    D    E    F
                OpCode.BRK, OpCode.ora, OpCode.___, OpCode.___, OpCode.___, OpCode.ora, OpCode.asl, OpCode.___, OpCode.php, OpCode.ora, OpCode.asl, OpCode.___, OpCode.___, OpCode.ora, OpCode.asl, OpCode.___, // 0
                OpCode.bpl, OpCode.ora, OpCode.___, OpCode.___, OpCode.___, OpCode.ora, OpCode.asl, OpCode.___, OpCode.CLC, OpCode.ora, OpCode.___, OpCode.___, OpCode.___, OpCode.ora, OpCode.asl, OpCode.___, // 1
                OpCode.jsr, OpCode.and, OpCode.___, OpCode.___, OpCode.bit, OpCode.and, OpCode.rol, OpCode.___, OpCode.plp, OpCode.and, OpCode.rol, OpCode.___, OpCode.bit, OpCode.and, OpCode.rol, OpCode.___, // 2
                OpCode.bmi, OpCode.and, OpCode.___, OpCode.___, OpCode.___, OpCode.and, OpCode.rol, OpCode.___, OpCode.SEC, OpCode.and, OpCode.___, OpCode.___, OpCode.___, OpCode.and, OpCode.rol, OpCode.___, // 3
                OpCode.rti, OpCode.eor, OpCode.___, OpCode.___, OpCode.___, OpCode.eor, OpCode.lsr, OpCode.___, OpCode.pha, OpCode.eor, OpCode.lsr, OpCode.___, OpCode.jmp, OpCode.eor, OpCode.lsr, OpCode.___, // 4
                OpCode.bvc, OpCode.eor, OpCode.___, OpCode.___, OpCode.___, OpCode.eor, OpCode.lsr, OpCode.___, OpCode.CLI, OpCode.eor, OpCode.___, OpCode.___, OpCode.___, OpCode.eor, OpCode.lsr, OpCode.___, // 5
                OpCode.rts, OpCode.ADC, OpCode.___, OpCode.___, OpCode.___, OpCode.ADC, OpCode.ror, OpCode.___, OpCode.pla, OpCode.ADC, OpCode.ror, OpCode.___, OpCode.jmp, OpCode.ADC, OpCode.ror, OpCode.___, // 6
                OpCode.bvs, OpCode.ADC, OpCode.___, OpCode.___, OpCode.___, OpCode.ADC, OpCode.ror, OpCode.___, OpCode.SEI, OpCode.ADC, OpCode.___, OpCode.___, OpCode.___, OpCode.ADC, OpCode.ror, OpCode.___, // 7
                OpCode.___, OpCode.STA, OpCode.___, OpCode.___, OpCode.STY, OpCode.STA, OpCode.STX, OpCode.___, OpCode.dey, OpCode.___, OpCode.TXA, OpCode.___, OpCode.STY, OpCode.STA, OpCode.STX, OpCode.___, // 8
                OpCode.bcc, OpCode.STA, OpCode.___, OpCode.___, OpCode.STY, OpCode.STA, OpCode.STX, OpCode.___, OpCode.TYA, OpCode.STA, OpCode.txs, OpCode.___, OpCode.___, OpCode.STA, OpCode.___, OpCode.___, // 9
                OpCode.LDY, OpCode.LDA, OpCode.LDX, OpCode.___, OpCode.LDY, OpCode.LDA, OpCode.LDX, OpCode.___, OpCode.TAY, OpCode.LDA, OpCode.TAX, OpCode.___, OpCode.LDY, OpCode.LDA, OpCode.LDX, OpCode.___, // A
                OpCode.bcs, OpCode.LDA, OpCode.___, OpCode.___, OpCode.LDY, OpCode.LDA, OpCode.LDX, OpCode.___, OpCode.CLV, OpCode.LDA, OpCode.tsx, OpCode.___, OpCode.LDY, OpCode.LDA, OpCode.LDX, OpCode.___, // B
                OpCode.cpy, OpCode.cmp, OpCode.___, OpCode.___, OpCode.cpy, OpCode.cmp, OpCode.dec, OpCode.___, OpCode.iny, OpCode.cmp, OpCode.dex, OpCode.___, OpCode.cpy, OpCode.cmp, OpCode.dec, OpCode.___, // C
                OpCode.bne, OpCode.cmp, OpCode.___, OpCode.___, OpCode.___, OpCode.cmp, OpCode.dec, OpCode.___, OpCode.CLD, OpCode.cmp, OpCode.___, OpCode.___, OpCode.___, OpCode.cmp, OpCode.dec, OpCode.___, // D
                OpCode.cpx, OpCode.SBC, OpCode.___, OpCode.___, OpCode.cpx, OpCode.SBC, OpCode.inc, OpCode.___, OpCode.inx, OpCode.SBC, OpCode.NOP, OpCode.___, OpCode.cpx, OpCode.SBC, OpCode.inc, OpCode.___, // E
                OpCode.beq, OpCode.SBC, OpCode.___, OpCode.___, OpCode.___, OpCode.SBC, OpCode.inc, OpCode.___, OpCode.SED, OpCode.SBC, OpCode.___, OpCode.___, OpCode.___, OpCode.SBC, OpCode.inc, OpCode.___  // F
    };
    }
    public class DAsm
    {
        //ASM6502 的代码在PRG芯片中，一个PRG芯片16K
        public static Instruction ReadCode(byte[] src, UInt32 offset)
        {
            if (offset >= src.Length)
                return null;
            Instruction t = Instruction.CreateFromByte(src[offset]);
            if (t.Size == 0)
                return null;
            else if (t.Size == 1)
                return t;
            else
            {
                if (src.Length <= t.Size + offset)
                {
                    return null;
                }
                else
                {//只有  1 2 3 三种情况
                    if (t.Size > 1)
                        t.data = src[offset + 1];
                    if (t.Size > 2)
                        t.data2 = src[offset + 2];
                    return t;
                }
            }
        }
    }
}
